package controller

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"gin-epg/internal/app/common/rsp"
	"gin-epg/internal/app/model"
	"gin-epg/internal/app/param"
	"gin-epg/internal/app/service"
	"gin-epg/internal/app/store"
	"github.com/iancoleman/orderedmap"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
)

// ToolConvPlaylist 处理从 任意格式文件 到任意格式文件转换 源格式支持txt m3u pls 目标格式支持txt m3u pls
func ToolConvPlaylist(c *gin.Context) {
	// 获取上传的文件
	file, err := c.FormFile("file")
	var tempFilePath string

	if err != nil {
		// 如果文件上传失败，尝试从 form 字段 content 中获取内容
		content := c.PostForm("content")
		if content == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get file or content"})
			return
		}

		// 保存内容到临时文件
		tempDir := os.TempDir()
		if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
			return
		}
		tempFilePath = filepath.Join(tempDir, "uploaded_content.txt")
		if err := os.WriteFile(tempFilePath, []byte(content), 0644); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save content to temp file"})
			return
		}
		defer os.Remove(tempFilePath) // 确保临时文件被删除
	} else {
		// 保存文件到系统临时目录
		tempDir := os.TempDir()
		if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
			return
		}
		tempFilePath = filepath.Join(tempDir, file.Filename)
		if err := c.SaveUploadedFile(file, tempFilePath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save uploaded file"})
			return
		}
		defer os.Remove(tempFilePath) // 确保临时文件被删除
	}

	// 获取目标格式
	dstFmt := c.PostForm("fmt")
	if dstFmt == "" {
		dstFmt = "m3u" // 默认目标格式为 m3u
	}

	// 获取方言 仅仅在fmt=txt 时可设置 dialect=diyp
	dialect := c.DefaultPostForm("dialect", "")

	var srcFmt string
	if file != nil {
		// 确定源格式
		srcFmt = strings.ToLower(filepath.Ext(file.Filename))
		// 如果 srcFmt 不在.txt .m3u .pls 时，默认为 ""
		if !strings.Contains(".txt.m3u.pls", srcFmt) {
			srcFmt = ""
		}
	} else {
		srcFmt = ""
	}

	if srcFmt == "" {
		// 如果无法从文件名确定源格式，可以读取文件第一行内容进行判断
		srcFmt, err = detectSourceFormat(tempFilePath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect source format"})
			return
		}
	}

	// 如果源格式和目标格式相同，直接返回原文件
	if srcFmt == "."+dstFmt {
		fileStream, err := os.Open(tempFilePath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
			return
		}
		defer fileStream.Close()

		// 设置响应头
		exportName := file.Filename
		// 对文件名进行 URL 编码（UTF-8）
		encodedFilename := url.QueryEscape(exportName)
		c.Header("Content-Type", "application/octet-stream")
		//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
		c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

		// 将文件流写入响应
		_, err = io.Copy(c.Writer, fileStream)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to response"})
			return
		}

		return
	}

	// 解析文件
	channels, err := parse(tempFilePath, srcFmt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse file"})
		return
	}

	// 生成目标格式文件
	fileStream, err := generate(channels, dstFmt, dialect)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate file"})
		return
	}
	defer fileStream.Close()

	// 设置响应头
	// 设置响应头
	exportName := "uploaded_content.txt" // 当 file 为 nil 时，使用默认文件名
	if file != nil {
		exportName = file.Filename[:len(file.Filename)-len(srcFmt)] + "." + dstFmt
	}
	// 对文件名进行 URL 编码（UTF-8）
	encodedFilename := url.QueryEscape(exportName)
	c.Header("Content-Type", "application/octet-stream")
	//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
	// 设置响应头，同时保留兼容性（RFC 5987）
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

	// 将文件流写入响应
	_, err = io.Copy(c.Writer, fileStream)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to response"})
		return
	}
}

// ToolConvPlaylistGet 处理从 URL 下载的文件到任意格式文件转换 源格式支持txt m3u pls 目标格式支持txt m3u pls
func ToolConvPlaylistGet(c *gin.Context) {
	// 获取查询参数中的 URL 和目标格式
	queryurl := c.Query("url")
	dstFmt := c.Query("fmt")
	if dstFmt == "" {
		dstFmt = "m3u" // 默认目标格式为 m3u
	}
	//获取方言
	dialect := c.DefaultQuery("dialect", "")

	// 创建 HTTP 客户端并设置 User-Agent
	client := &http.Client{}
	req, err := http.NewRequest("GET", queryurl, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("User-Agent", "okhttp/3.15")

	// 发送请求并获取响应
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download file"})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download file, status code: " + resp.Status})
		return
	}

	// 保存文件到系统临时目录
	tempDir := os.TempDir()
	if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
		return
	}
	tempFilePath := filepath.Join(tempDir, "downloaded_file")
	file, err := os.Create(tempFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp file"})
		return
	}
	defer file.Close()

	// 将响应体内容写入临时文件
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to temp directory"})
		return
	}

	// 确定源格式
	srcFmt, err := detectSourceFormat(tempFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect source format"})
		return
	}

	// 如果源格式和目标格式相同，直接返回原文件
	if srcFmt == "."+dstFmt {
		fileStream, err := os.Open(tempFilePath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
			return
		}
		defer fileStream.Close()

		// 设置响应头
		exportName := "downloaded_file" + "." + dstFmt
		// 对文件名进行 URL 编码（UTF-8）
		encodedFilename := url.QueryEscape(exportName)
		c.Header("Content-Type", "application/octet-stream")
		//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
		c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

		// 将文件流写入响应
		_, err = io.Copy(c.Writer, fileStream)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to response"})
			return
		}

		// 删除临时文件
		if err := os.Remove(tempFilePath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
			return
		}
		return
	}

	// 解析文件
	channels, err := parse(tempFilePath, srcFmt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse file"})
		return
	}

	// 生成目标格式文件
	fileStream, err := generate(channels, dstFmt, dialect)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate file"})
		return
	}
	defer fileStream.Close()

	// 设置响应头
	exportName := "downloaded_file" + "." + dstFmt
	// 对文件名进行 URL 编码（UTF-8）
	encodedFilename := url.QueryEscape(exportName)
	c.Header("Content-Type", "application/octet-stream")
	//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

	// 将文件流写入响应
	_, err = io.Copy(c.Writer, fileStream)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to response"})
		return
	}

	// 删除临时文件
	if err := os.Remove(tempFilePath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
		return
	}
}

// ToolPlaylist2m3uMatchGet 处理从 URL 下载的 .txt/.m3u/.pls 文件到 .m3u 文件的转换，增加扩展属性
func ToolPlaylist2m3uMatchGet(c *gin.Context) {
	// 获取查询参数中的 URL 和目标格式
	queryurl := c.Query("url")
	if queryurl == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing URL parameter"})
		return
	}

	// 创建 HTTP 客户端并设置 User-Agent
	client := &http.Client{}
	req, err := http.NewRequest("GET", queryurl, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("User-Agent", "okhttp/3.15")

	// 发送请求并获取响应
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download file"})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download file, status code: " + resp.Status})
		return
	}

	// 保存文件到系统临时目录
	tempDir := os.TempDir()
	if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
		return
	}
	tempFilePath := filepath.Join(tempDir, "downloaded_file")
	file, err := os.Create(tempFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp file"})
		return
	}
	defer file.Close()

	// 将响应体内容写入临时文件
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write file to temp directory"})
		return
	}

	// 确定源格式
	srcFmt, err := detectSourceFormat(tempFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect source format"})
		return
	}

	// 解析文件
	channels, err := parsePlaylist(tempFilePath, srcFmt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse file"})
		return
	}

	// 生成目标格式文件（带扩展属性的 M3U）
	m3uFileStream, err := service.GenerateM3UWithExtendedAttributes(channels)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate M3U file"})
		return
	}
	defer m3uFileStream.Close()

	// 设置响应头
	exportName := "converted_playlist.m3u"
	// 对文件名进行 URL 编码（UTF-8）
	encodedFilename := url.QueryEscape(exportName)
	c.Header("Content-Type", "application/octet-stream")
	//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

	// 将 M3U 文件流写入响应
	_, err = io.Copy(c.Writer, m3uFileStream)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write M3U file to response"})
		return
	}

	// 删除临时文件
	if err := os.Remove(tempFilePath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
		return
	}
}

// detectSourceFormat 根据文件内容检测源格式
func detectSourceFormat(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	if scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#EXTM3U") {
			return ".m3u", nil
		} else if strings.HasPrefix(line, "[playlist]") {
			return ".pls", nil
		}
	}
	if err := scanner.Err(); err != nil {
		return "", err
	}

	// 默认返回 .txt
	return ".txt", nil
}

// parse 根据源格式解析文件并返回 Channel 列表
func parse(filePath, srcFmt string) ([]model.PlaylistChannel, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var channels []model.PlaylistChannel
	var currentGenre string
	scanner := bufio.NewScanner(file)

	switch srcFmt {
	case ".txt":
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" {
				continue
			}
			if strings.HasSuffix(line, "#genre#") {
				currentGenre = strings.TrimSuffix(line, ",#genre#")
				continue
			}
			parts := strings.SplitN(line, ",", 2)
			if len(parts) != 2 {
				continue
			}
			channels = append(channels, model.PlaylistChannel{Name: parts[0], URL: parts[1], GroupName: currentGenre})
		}
	case ".m3u":
		var channel model.PlaylistChannel
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" || strings.HasPrefix(line, "#EXTM3U") {
				continue
			}
			if strings.HasPrefix(line, "#EXTINF") {
				durationAndTitle := strings.TrimPrefix(line, "#EXTINF:")
				parts := strings.SplitN(durationAndTitle, ",", 2)
				if len(parts) != 2 {
					continue
				}
				channel.Name = parts[1]

				if strings.Contains(line, "group-title=") {
					// 提取 group-title 的值
					groupTitle := strings.Split(line, "group-title=")[1]
					groupTitle = strings.Split(groupTitle, ",")[0]
					groupTitle = strings.Trim(groupTitle, "\"")
					channel.GroupName = groupTitle
				}
				continue
			}

			channel.URL = line
			channels = append(channels, channel)
			channel = model.PlaylistChannel{} // 重置 channel
		}
	case ".pls":
		var channel model.PlaylistChannel
		entryIndex := 1
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" || strings.HasPrefix(line, "[playlist]") || strings.HasPrefix(line, "NumberOfEntries=") || strings.HasPrefix(line, "Version=") {
				continue
			}
			if strings.HasPrefix(line, "File") {
				channel.URL = strings.TrimPrefix(line, fmt.Sprintf("File%d=", entryIndex))
				scanner.Scan() // 读取下一行，即 Title 行
				titleLine := strings.TrimSpace(scanner.Text())
				if !strings.HasPrefix(titleLine, fmt.Sprintf("Title%d=", entryIndex)) {
					continue
				}
				channel.Name = strings.TrimPrefix(titleLine, fmt.Sprintf("Title%d=", entryIndex))
				channels = append(channels, channel)
				entryIndex++
				channel = model.PlaylistChannel{} // 重置 channel
			}
		}
	default:
		return nil, fmt.Errorf("unsupported source format")
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return channels, nil
}

// generate 根据目标格式生成文件流
func generate(channels []model.PlaylistChannel, dstFmt string, dialect string) (io.ReadCloser, error) {
	var content strings.Builder

	switch dstFmt {
	case "txt":
		if dialect == "diyp" {
			// 使用 orderedmap 来存储分组名及其对应的频道列表
			groupMap := orderedmap.New()

			// 将频道按分组名分类
			for _, channel := range channels {
				if value, ok := groupMap.Get(channel.GroupName); ok {
					channels := value.([]model.PlaylistChannel)
					channels = append(channels, channel)
					groupMap.Set(channel.GroupName, channels)
				} else {
					groupMap.Set(channel.GroupName, []model.PlaylistChannel{channel})
				}
			}
			// 按分组名顺序写入内容
			keys := groupMap.Keys()
			for _, key := range keys {
				groupName := key
				groupChannelsInterface, exists := groupMap.Get(key)
				if !exists {
					fmt.Printf("Key %s does not exist in the map\n", key)
					continue
				}

				// 将 interface{} 断言为 []model.PlaylistChannel
				groupChannels, ok := groupChannelsInterface.([]model.PlaylistChannel)
				if !ok {
					fmt.Printf("Value for key %s is not of type []model.PlaylistChannel\n", key)
					continue
				}

				content.WriteString(groupName + ",#genre#\n")
				for _, channel := range groupChannels {
					content.WriteString(fmt.Sprintf("%s,%s\n", channel.Name, channel.URL))
				}
				content.WriteString("\n") // 分组之间添加空行
			}
		} else {
			for _, channel := range channels {
				content.WriteString(fmt.Sprintf("%s,%s\n", channel.Name, channel.URL))
			}
		}
	case "m3u":
		content.WriteString("#EXTM3U\n")
		hasGroup := false
		if len(channels) > 0 && channels[0].GroupName != "" {
			hasGroup = true
		}

		for _, channel := range channels {
			if hasGroup {
				content.WriteString(fmt.Sprintf("#EXTINF:-1 group-title=\"%s\",%s\n%s\n", channel.GroupName, channel.Name, channel.URL))
			} else {
				content.WriteString(fmt.Sprintf("#EXTINF:-1,%s\n%s\n", channel.Name, channel.URL))
			}
		}
	case "pls":
		content.WriteString("[playlist]\n")
		entryIndex := 1
		for _, channel := range channels {
			content.WriteString(fmt.Sprintf("File%d=%s\n", entryIndex, channel.URL))
			content.WriteString(fmt.Sprintf("Title%d=%s\n", entryIndex, channel.Name))
			entryIndex++
		}
		content.WriteString(fmt.Sprintf("NumberOfEntries=%d\n", entryIndex-1))
		content.WriteString("Version=2\n")
	default:
		return nil, fmt.Errorf("unsupported destination format")
	}

	// 生成临时文件路径
	tempFilePath := filepath.Join(os.TempDir(), "temp."+dstFmt)
	if err := os.WriteFile(tempFilePath, []byte(content.String()), 0644); err != nil {
		return nil, err
	}

	// 返回文件的读取流
	return os.Open(tempFilePath)
}

// ToolPlaylist2m3uMatch 处理从 .txt .m3u .pls 文件到 .m3u 文件的转换，增加扩展属性
// 下一个接口增加 diyp_txt 格式
// 央视,#genre#
// CCTV1,http://39.134.24.162/3221225804/1.m3u8
func ToolPlaylist2m3uMatch(c *gin.Context) {
	// 获取上传的文件
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get file"})
		return
	}

	// 保存文件到系统临时目录
	tempDir := os.TempDir()
	if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
		return
	}
	tempFilePath := filepath.Join(tempDir, file.Filename)
	if err := c.SaveUploadedFile(file, tempFilePath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save uploaded file"})
		return
	}

	// 确定源格式
	srcFmt := strings.ToLower(filepath.Ext(file.Filename))
	if srcFmt == "" {
		// 如果无法从文件名确定源格式，可以读取文件第一行内容进行判断
		srcFmt, err = detectSourceFormat(tempFilePath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to detect source format"})
			return
		}
	}

	// 解析文件
	channels, err := parsePlaylist(tempFilePath, srcFmt)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse file"})
		return
	}

	// 生成目标格式文件
	m3uFileStream, err := service.GenerateM3UWithExtendedAttributes(channels)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate M3U file"})
		return
	}
	defer m3uFileStream.Close()

	// 设置响应头
	exportName := file.Filename[:len(file.Filename)-len(srcFmt)] + ".m3u"
	// 对文件名进行 URL 编码（UTF-8）
	encodedFilename := url.QueryEscape(exportName)
	c.Header("Content-Type", "application/octet-stream")
	//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

	// 将 M3U 文件流写入响应
	_, err = io.Copy(c.Writer, m3uFileStream)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write M3U file to response"})
		return
	}

	// 删除临时文件
	if err := os.Remove(tempFilePath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
		return
	}
}

// parsePlaylist 根据源格式解析文件并返回 Channel 列表
func parsePlaylist(filePath, srcFmt string) ([]model.PlaylistChannel, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var channels []model.PlaylistChannel
	scanner := bufio.NewScanner(file)

	switch srcFmt {
	case ".txt":
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" {
				continue
			}
			parts := strings.SplitN(line, ",", 2)
			if len(parts) != 2 {
				continue
			}
			channels = append(channels, model.PlaylistChannel{Name: parts[0], URL: parts[1]})
		}
	case ".m3u":
		var channel model.PlaylistChannel
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" || strings.HasPrefix(line, "#EXTM3U") {
				continue
			}
			if strings.HasPrefix(line, "#EXTINF") {
				durationAndTitle := strings.TrimPrefix(line, "#EXTINF:")
				parts := strings.SplitN(durationAndTitle, ",", 2)
				if len(parts) != 2 {
					continue
				}
				channel.Name = parts[1]
				continue
			}
			channel.URL = line
			channels = append(channels, channel)
			channel = model.PlaylistChannel{} // 重置 channel
		}
	case ".pls":
		var channel model.PlaylistChannel
		entryIndex := 1
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			if line == "" || strings.HasPrefix(line, "[playlist]") || strings.HasPrefix(line, "NumberOfEntries=") || strings.HasPrefix(line, "Version=") {
				continue
			}
			if strings.HasPrefix(line, "File") {
				channel.URL = strings.TrimPrefix(line, fmt.Sprintf("File%d=", entryIndex))
				scanner.Scan() // 读取下一行，即 Title 行
				titleLine := strings.TrimSpace(scanner.Text())
				if !strings.HasPrefix(titleLine, fmt.Sprintf("Title%d=", entryIndex)) {
					continue
				}
				channel.Name = strings.TrimPrefix(titleLine, fmt.Sprintf("Title%d=", entryIndex))
				channels = append(channels, channel)
				entryIndex++
				channel = model.PlaylistChannel{} // 重置 channel
			}
		}
	default:
		return nil, fmt.Errorf("unsupported source format")
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return channels, nil
}

// ToolTxt2tvbox txt链接转json
func ToolTxt2tvbox(c *gin.Context) {
	// 获取查询参数中的 URL
	url := c.Query("url")

	// 调用服务方法生成 JSON
	jsonResponse := service.LivetxtToTvbox(url)

	// 返回 JSON 响应
	c.JSON(http.StatusOK, jsonResponse)
}

// ToolMergeTxt 合并两个txt
func ToolMergeTxt(c *gin.Context) {
	// 设置文件上传限制
	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10<<20) // 10 MB

	// 解析表单数据
	err := c.Request.ParseMultipartForm(10 << 20)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse form data"})
		return
	}

	// 获取上传的文件
	file1, err1 := c.FormFile("file1")
	file2, err2 := c.FormFile("file2")

	if err1 != nil || err2 != nil || file1 == nil || file2 == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing file uploads"})
		return
	}

	// 保存上传的文件到系统临时目录
	tempDir := os.TempDir()
	if err := os.MkdirAll(tempDir, os.ModePerm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
		return
	}
	dst1 := filepath.Join(tempDir, file1.Filename)
	dst2 := filepath.Join(tempDir, file2.Filename)

	if err := c.SaveUploadedFile(file1, dst1); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file1"})
		return
	}

	if err := c.SaveUploadedFile(file2, dst2); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file2"})
		return
	}

	// 使用 orderedmap 代替 map
	channelMap := orderedmap.New()

	// 读取并合并文件内容
	readAndMergeFile(dst1, channelMap)
	readAndMergeFile(dst2, channelMap)

	// 生成合并后的文件内容
	var mergedContent strings.Builder
	keys := channelMap.Keys()
	for _, k := range keys {
		v, exists := channelMap.Get(k)
		if !exists {
			fmt.Printf("Key %s does not exist in the map\n", k)
			continue
		}
		name, ok := v.(string)
		if !ok {
			fmt.Printf("Value for key %s is not a string\n", k)
			continue
		}
		mergedContent.WriteString(fmt.Sprintf("%s,%s\n", name, k))
	}

	// 生成 TXT 文件路径
	txtFilePath := filepath.Join(os.TempDir(), "merge.txt")
	if err := os.WriteFile(txtFilePath, []byte(mergedContent.String()), 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write TXT file"})
		return
	}

	// 返回 TXT 文件的读取流
	txtFileStream, err := os.Open(txtFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open TXT file"})
		return
	}
	defer txtFileStream.Close()

	// 设置响应头
	exportName := "merge.txt"
	// 对文件名进行 URL 编码（UTF-8）
	encodedFilename := url.QueryEscape(exportName)
	c.Header("Content-Type", "application/octet-stream")
	//c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exportName))
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", encodedFilename, encodedFilename))

	// 将合并后的文件流发送给客户端
	_, err = io.Copy(c.Writer, txtFileStream)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send merged file"})
		return
	}

	// 删除临时文件
	if err := os.Remove(dst1); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file1"})
		return
	}

	if err := os.Remove(dst2); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file2"})
		return
	}

	if err := os.Remove(txtFilePath); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
		return
	}
}

// 辅助函数：读取文件并合并到 orderedmap
func readAndMergeFile(filePath string, channelMap *orderedmap.OrderedMap) {
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Printf("Failed to open file %s: %v\n", filePath, err)
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}
		parts := strings.SplitN(line, ",", 2)
		if len(parts) != 2 {
			continue
		}
		channelName := parts[0]
		url := parts[1]
		_, exists := channelMap.Get(url)
		if !exists {
			channelMap.Set(url, channelName)
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Printf("Failed to read file %s: %v\n", filePath, err)
	}
}

// ToolMcPtList 组播平台列表
func ToolMcPtList(c *gin.Context) {
	obj := param.QueryToolMulticastPlatformParam{}
	c.BindJSON(&obj)
	todoList, err := service.GetToolMulticastPlatformList(&obj)
	if err != nil {
		rsp.Error(c, err.Error())
	} else {
		rsp.Success(c, "请求成功", todoList)
	}
}

// ToolChListByPt 组播频道平台列表by平台
func ToolChListByPt(c *gin.Context) {
	// 获取查询参数中的 URL
	platformId := c.Query("platformId")
	todoList, err := service.GetToolMulticastChannelByPlatformId(platformId)
	if err != nil {
		rsp.Error(c, err.Error())
	} else {
		rsp.Success(c, "请求成功", todoList)
	}
}

// ToolChTxtListByPt 组播频道平台列表by平台，导出txt
func ToolChTxtListByPt(c *gin.Context) {
	// 获取查询参数
	platformIdStr := c.Query("platformId")
	fwDp := c.Query("fwDp")

	// 获取平台信息
	platform, err := service.GetToolMulticastPlatformById(platformIdStr)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get platform information"})
		return
	}

	// 检查平台是否存在
	if platform == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Platform not found"})
		return
	}

	// 确保 platform.LastUpdate 是一个 time.Time 类型
	lastUpdateDate := platform.LastUpdate.FormatDate()

	// 生成文件名
	fileName := fmt.Sprintf("%s_%s_%s_%s_%s_%s", platformIdStr, platform.Province, platform.City, platform.Operator, platform.Platform, lastUpdateDate)
	exportName := url.QueryEscape(fileName + ".txt")

	// 获取组播频道列表
	channels, err := service.GetToolMulticastChannelByPlatformId(platformIdStr)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get channel list"})
		return
	}

	// 生成 TXT 内容
	var txtContent strings.Builder
	for _, channel := range channels {
		url := channel.URL
		if fwDp != "" {
			url = service.GetForwardUrl(fwDp, url)
		}
		txtContent.WriteString(fmt.Sprintf("%s,%s\n", channel.Channel, url))
	}

	// 生成 TXT 文件路径
	txtFilePath := filepath.Join(os.TempDir(), "temp.txt")
	if err := os.WriteFile(txtFilePath, []byte(txtContent.String()), 0644); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to write TXT file"})
		return
	}

	// 设置响应头
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s; filename*=utf-8''%s", exportName, exportName))

	// 将 TXT 文件流发送给客户端
	txtFileStream, err := os.Open(txtFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open TXT file"})
		return
	}

	// 确保文件句柄在文件读取完成后立即关闭
	if _, err := io.Copy(c.Writer, txtFileStream); err != nil {
		txtFileStream.Close()
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send TXT file"})
		return
	}
	txtFileStream.Close()

	// 删除临时文件
	if err := os.Remove(txtFilePath); err != nil {
		// 添加日志输出以帮助调试
		fmt.Printf("Failed to delete temporary file %s: %v\n", txtFilePath, err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete temporary file"})
		return
	}

	// 成功响应
	c.Status(http.StatusOK)
}

// 批量下载mcchlist
func ToolBatchDownloadMcchlist(c *gin.Context) {
	// 查询平台列表
	platformList, err := service.GetToolMulticastPlatformList(&param.QueryToolMulticastPlatformParam{})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get platform list"})
		return
	}

	// 创建临时目录
	tempDir := filepath.Join(os.TempDir(), "multicast")
	err = os.MkdirAll(tempDir, os.ModePerm)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
		return
	}
	defer os.RemoveAll(tempDir) // 清理临时目录

	// 遍历每个平台，下载并保存 mcchlist 到临时目录
	for _, platform := range platformList {
		platformId := platform.Id
		channels, err := service.GetToolMulticastChannelByPlatformId(strconv.Itoa(platformId))
		if err != nil {
			fmt.Printf("Failed to get channel list for platform %s: %v\n", platformId, err)
			continue
		}

		// 生成文件名
		lastUpdateDate := platform.LastUpdate.FormatDate()
		fileName := fmt.Sprintf("%d_%s_%s_%s_%s_%s", platformId, platform.Province, platform.City, platform.Operator, platform.Platform, lastUpdateDate)
		txtFilePath := filepath.Join(tempDir, fileName+".txt")

		// 生成 TXT 内容
		var txtContent strings.Builder
		for _, channel := range channels {
			url := channel.URL
			txtContent.WriteString(fmt.Sprintf("%s,%s\n", channel.Channel, url))
		}

		// 写入 TXT 文件
		if err := os.WriteFile(txtFilePath, []byte(txtContent.String()), 0644); err != nil {
			fmt.Printf("Failed to write TXT file %s: %v\n", txtFilePath, err)
			continue
		}
	}

	// 压缩临时目录
	zipFilePath := filepath.Join(os.TempDir(), "multicast.zip")
	err = service.ZipDirectory(tempDir, zipFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to zip directory"})
		return
	}

	// 将压缩文件发送给客户端
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename=multicast.zip")
	c.Header("Content-Length", fmt.Sprintf("%d", service.GetFileSize(zipFilePath)))

	file, err := os.Open(zipFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open zip file"})
		return
	}
	defer file.Close()

	_, err = io.Copy(c.Writer, file)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send zip file"})
		return
	}
}

// 批量下载logo
func ToolBatchDownloadLogo(c *gin.Context) {
	// 初始化 OSS 存储服务
	ossService := store.GlobalStoreService

	// 列出 OSS 中的 logo 文件
	logoPrefix := "logo/"
	logoFiles, err := ossService.ListDirectory(logoPrefix)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	// 创建临时目录
	tempDir := filepath.Join(os.TempDir(), "logos")
	err = os.MkdirAll(tempDir, os.ModePerm)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	defer os.RemoveAll(tempDir) // 清理临时目录

	// 下载 logo 文件到临时目录
	for _, fileInfo := range logoFiles {
		if !fileInfo.IsDir {
			remotePath := logoPrefix + fileInfo.Name
			localPath := filepath.Join(tempDir, fileInfo.Name)
			err := ossService.DownloadToFile(remotePath, localPath)
			if err != nil {
				fmt.Printf("Failed to download logo %s: %v\n", fileInfo.Name, err) // 添加错误提示
			}
		}
	}

	// 压缩临时目录
	zipFilePath := filepath.Join(os.TempDir(), "logos.zip")
	err = service.ZipDirectory(tempDir, zipFilePath)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	// 将压缩文件发送给客户端
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename=logos.zip")
	c.Header("Content-Length", fmt.Sprintf("%d", service.GetFileSize(zipFilePath)))

	file, err := os.Open(zipFilePath)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	defer file.Close()

	_, err = io.Copy(c.Writer, file)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
}

// 批量下载logo
func ToolBatchDownloadLogoByProvince(c *gin.Context) {
	// 获取省份参数
	province := c.Query("province")

	// 初始化 OSS 存储服务
	ossService := store.GlobalStoreService

	// 查询 EPG 频道列表
	params := &param.QueryEpgChannelListParam{}
	if province != "" {
		params.Province = province
	}
	epgChannelList, err := service.GetEpgChannelList(params)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	// 创建临时目录
	tempDir := filepath.Join(os.TempDir(), "logos")
	err = os.MkdirAll(tempDir, os.ModePerm)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	defer os.RemoveAll(tempDir) // 清理临时目录

	// 下载 logo 文件到临时目录
	for _, channel := range epgChannelList {
		logoName := channel.Name + ".png" // 假设 logo 文件名为频道名称加上 .png 后缀
		remotePath := "logo/" + logoName
		localPath := filepath.Join(tempDir, logoName)
		err := ossService.DownloadToFile(remotePath, localPath)
		if err != nil {
			fmt.Printf("Failed to download logo %s: %v\n", logoName, err) // 添加错误提示
		}
	}

	// 压缩临时目录
	zipFilePath := filepath.Join(os.TempDir(), "logos.zip")
	err = service.ZipDirectory(tempDir, zipFilePath)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}

	// 将压缩文件发送给客户端
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename=logos.zip")
	c.Header("Content-Length", fmt.Sprintf("%d", service.GetFileSize(zipFilePath)))

	file, err := os.Open(zipFilePath)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	defer file.Close()

	_, err = io.Copy(c.Writer, file)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
}

// TvboxUrlToStr 处理从 URL 下载的图片文件并将其转换为字符串
func TvboxUrlToStr(c *gin.Context) {
	// 获取查询参数中的 URL
	url := c.Query("url")
	if url == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get URL"})
		return
	}

	// 创建 HTTP 客户端并设置 User-Agent
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create request"})
		return
	}
	req.Header.Set("User-Agent", "okhttp/3.15")

	// 发送请求并获取响应
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download image"})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to download image, status code: " + resp.Status})
		return
	}

	// 调用 imgToStr 处理文件并获取解码后的字符串
	decodedStr, err := imgToStr(resp.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process file"})
		return
	}

	// 返回成功响应和解码后的字符串
	c.String(http.StatusOK, decodedStr)
}

// tvbox加解密参考 https://duo.hz.cz/mao/

// TvboxImgToStr 处理上传的图片文件并将其转换为字符串
func TvboxImgToStr(c *gin.Context) {
	// 获取上传的文件
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get file"})
		return
	}

	// 打开上传的文件
	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer src.Close()

	// 调用 imgToStr 处理文件并获取解码后的字符串
	decodedStr, err := imgToStr(src)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process file"})
		return
	}

	// 返回成功响应和解码后的字符串
	c.String(http.StatusOK, decodedStr)
}

// imgToStr processes the uploaded image file and returns the decoded string after the last '**'.
func imgToStr(file io.Reader) (string, error) {
	content, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}
	strContent := ab2Str(content)
	lastIndex := strings.LastIndex(strContent, "**")
	if lastIndex != -1 {
		strContent = strContent[lastIndex+2:] // 截取最后一个 ** 后面的部分
	}
	decodedContent, err := base64.StdEncoding.DecodeString(strContent)
	if err != nil {
		return "", err
	}
	return string(decodedContent), nil
}

// ab2Str converts byte slice to string.
func ab2Str(ab []byte) string {
	return string(ab)
}

// TvboxAes128CbcDecode 处理从 URL 下载的图片文件并将其转换为字符串
func TvboxAes128CbcDecode(c *gin.Context) {
	// 获取查询参数中的 URL
	ciphertext := c.Query("ciphertext")
	// 密文
	//ciphertext := "24233132333435362324951f72743bda49a6e41b1292b88f25ce31373334313732323532303435"
	//hex转字符串
	//ciphertextHex, _ := hex.DecodeString(ciphertext)

	cbc, err := decryptAesCBC(ciphertext)
	if err != nil {
		fmt.Printf("Error decrypting: %v\n", err)
		return
	}

	fmt.Printf("Decrypted plaintext: %s\n", cbc)

	// 返回成功响应和解码后的字符串
	c.String(http.StatusOK, cbc)
}

func decryptAesCBC(encryptedData string) (string, error) {
	//key前缀
	prefixKeyCode := "$#"
	//key后缀
	suffixKeyCode := "#$"

	//prefixKeyCode 编码成 转16进制字符串
	prefixKeyCodeHex := hex.EncodeToString([]byte(prefixKeyCode))
	suffixKeyCodeHex := hex.EncodeToString([]byte(suffixKeyCode))

	// 找到 prefixKeyIndex 的位置
	prefixKeyIndex := strings.Index(encryptedData, prefixKeyCodeHex)
	if prefixKeyIndex == -1 {
		return "", fmt.Errorf("prefixKeyIndex code not found")
	}

	// 找到 suffixKeyIndex 的位置
	suffixKeyIndex := strings.Index(encryptedData, suffixKeyCodeHex)
	if suffixKeyIndex == -1 {
		return "", fmt.Errorf("suffix code not found")
	}

	//提取 key iv 密文
	pwdInHex := encryptedData[prefixKeyIndex+len(prefixKeyCodeHex) : suffixKeyIndex]
	roundtimeInHex := encryptedData[len(encryptedData)-26:]
	encryptedTextInHex := encryptedData[suffixKeyIndex+len(suffixKeyCodeHex) : len(encryptedData)-26]

	// 转换 roundTime 和 pwd
	roundTime, err := hexToString(roundtimeInHex)
	if err != nil {
		return "", err
	}
	pwd, err := hexToString(pwdInHex)
	if err != nil {
		return "", err
	}

	// 填充 IV 和 pkBlocks 到 16 字节
	iv := padTo16Bytes(roundTime)
	pkBlocks := padTo16Bytes(pwd)

	// 解密
	block, err := aes.NewCipher(pkBlocks)
	if err != nil {
		return "", err
	}

	// 创建 CBC 模式的解密器
	mode := cipher.NewCBCDecrypter(block, iv)

	// 将加密文本从十六进制转换为字节
	ciphertext, err := hex.DecodeString(encryptedTextInHex)
	if err != nil {
		return "", err
	}

	// 解密
	plaintext := make([]byte, len(ciphertext))
	mode.CryptBlocks(plaintext, ciphertext)

	// 去除填充
	plaintext, err = unpadPKCS7(plaintext)
	if err != nil {
		return "", err
	}

	decryptedData := string(plaintext)
	fmt.Println(decryptedData)
	return decryptedData, nil
}

// 将十六进制字符串转换为普通字符串
func hexToString(hexStr string) (string, error) {
	data, err := hex.DecodeString(hexStr)
	if err != nil {
		return "", err
	}
	return string(data), nil
}

// 将字符串填充到 16 字节，如果超过16字节则返回原字符串的字节数组
func padTo16Bytes(str string) []byte {
	if len(str) >= 16 {
		return []byte(str)
	}
	padding := 16 - len(str)%16
	padded := str + strings.Repeat("0", padding)
	return []byte(padded)
}

// 去除 PKCS7 填充
func unpadPKCS7(data []byte) ([]byte, error) {
	length := len(data)
	if length == 0 {
		return nil, fmt.Errorf("data is empty")
	}
	unpadding := int(data[length-1])
	if unpadding > 16 || unpadding == 0 {
		return nil, fmt.Errorf("invalid padding")
	}
	for i := 1; i <= unpadding; i++ {
		if data[length-i] != byte(unpadding) {
			return nil, fmt.Errorf("invalid padding")
		}
	}
	return data[:length-unpadding], nil
}
